package com.gxa.jjty.phy.controller;


import com.gxa.jjty.core.common.JsonResult;
import com.gxa.jjty.core.exception.BusinessException;
import lombok.extern.log4j.Log4j2;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Map;
import java.util.Set;

/**
 * 本质上,就是一个aop的通知,专门用于加强异常出现的时候的抛出通知
 * 在使用log的类中，只需要利用Lombok提供的注解 @Log4j2
 * @author bill
 * @date 2021/10/21 15:20
 */
@RestController
@ControllerAdvice
@Log4j2
public class ExceptionController {
    /**
     * 当有异常发生,抛出通知介入到目标操作
     * 并把异常对象注入到参数Exception中
     * value 表示什么类型的错误或异常才会被处理
     * 我们选择使用Throwable.class(Error和Exception都要记录)
     */


    /**
     * 处理第一种校验异常: 将异常信息拼接,返回给前端显示
     * @param errors 把异常信息注入到spring自己的Errors对象中
     * @return
     */
    @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
    public Map handleBindException(Errors errors){
        JsonResult jsonResult = new JsonResult();

        jsonResult.setCode("0");
        StringBuilder stringBuilder = new StringBuilder();
        String errorMessage ;

        // 使用lambda表达式对Errors中的对象遍历
        errors.getAllErrors().forEach(error->{
            stringBuilder.append(error.getDefaultMessage()).append(",");
        });

        // 处理末尾的逗号
        errorMessage = stringBuilder.toString().substring(0, stringBuilder.toString().length()-1);
        jsonResult.setMsg(errorMessage);
        return jsonResult.getDataMap();
    }

    /**
     * 处理第二种校验异常: 将异常信息拼接,返回给前端显示
     * @param exception
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Map handleConstraintViolationException(ConstraintViolationException exception) {
        StringBuilder errorInfo = new StringBuilder();
        String errorMessage ;

        Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
        for (ConstraintViolation<?> item : violations) {
            errorInfo.append(item.getMessage()).append(",");
        }
        // 处理最后多余的逗号
        errorMessage = errorInfo.toString().substring(0, errorInfo.toString().length()-1);
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("0");
        jsonResult.setMsg(errorMessage);
        return jsonResult.getDataMap();
    }
    /**
     * 业务异常
     * @param e
     */
    @ExceptionHandler(BusinessException.class)
    public Map businessExceptionHandler(BusinessException e){
        // 具体操作和三阶段一致: 自定义异常就返给前端

        String code = e.getExceptionCode().getCode();
        String message = e.getExceptionCode().getMsg();

        // 处理异常信息
        // 通过业务异常对象获取ExceptionCode的枚举对象
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode(code);
        jsonResult.setMsg(message);

        return jsonResult.getDataMap();
    }


    /**
     * 系统异常
     * @param e
     */
    @ExceptionHandler(Exception.class)
    public Map exceptionHandler(Exception e){
        // 系统异常记录到日志文件(log4j)
        // FIXME  利用第三方日志完成设计 完善的操作
        // TODO 要做的操作

        StringBuilder builder = new StringBuilder();
        //分割线方便阅读
        builder.append("-----------------begin-----------------------");
        // 先记录异常对象的toString状态: 包括异常类型和异常原因
        builder.append(e);

        // 遍历栈中的异常轨迹: 采用遍历方式
        StackTraceElement[] stackTrace = e.getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            builder.append(stackTraceElement);
        }

        //分割线方便阅读
        builder.append("-------------------end---------------------");

        // error/info/ 都支持对象的直接写入日志文件，会自动调用toString()方法
        log.error(builder);

        // 返回前端提示
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("500");
        jsonResult.setMsg("服务器开小差了,请稍后再试");

//        测试使用
//        jsonResult.setMsg(e.getMessage());
        return jsonResult.getDataMap();
    }
}
